package models;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.List;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.persistence.PostLoad;
import javax.persistence.PrePersist;


import org.apache.commons.beanutils.DynaBean;
import com.google.appengine.api.NamespaceManager;
import controllers.Security;
import play.Logger;
import play.data.validation.Required;
import play.data.validation.Valid;
import siena.Filter;
import siena.Generator;
import siena.Index;
import siena.Model;
import siena.Query;
import utils.passwd.PasswdUtils;
import siena.*;
import siena.embed.Embedded;
import siena.embed.Embedded.Mode;
import siena.core.Aggregated;
import siena.core.Many;
import siena.core.One;
import siena.core.Owned;
import siena.core.lifecycle.PreSave;
import siena.core.lifecycle.PreInsert;
import siena.core.lifecycle.PostFetch;
import siena.core.lifecycle.PreFetch;
import siena.core.lifecycle.PostDelete;
import siena.core.lifecycle.PreUpdate;;

public class User extends BaseModel {
	
	@Required
	@NotNull
	public String username;
	
	@Required
	@NotNull
	public String password;
	
	public String oldPassword;
	
	public Boolean access;
	
	@Embedded(mode=Mode.NATIVE)
	@Valid
	@NotNull
	@Required
	public BasicInformation basic_information;
	
	public transient List<String> securityRoles;
	
	@PreSave
	@PreInsert
	@PreUpdate
	public void encodePassword() throws UnsupportedEncodingException, NoSuchAlgorithmException {
		// Only updates the password when is null or its different to the oldPassword
		if((this.password == null && this.oldPassword == null) ||
		   (this.password != null && !this.password.equals(oldPassword))) {
				String encodedPassword = PasswdUtils.encodePassword(this.password);
				this.password = encodedPassword;
				this.oldPassword = this.password;
		}
	}
	
	@PostFetch
	public void loadProfiles() {
		this.securityRoles = new ArrayList<String>();
		if(this.isAdmin()) {
			this.securityRoles.add(Security.ADMIN_ROLE);
		} else if(this.isFamiliar()) {
			this.securityRoles.add(Security.FAMILIAR_ROLE);
		} else {
			this.securityRoles.add(Security.INTERNED_ROLE);
		}
	}
	
	@PostDelete
	public void deleteJobs() {
		UserProfile.delete(this);
		InternshipFamiliar.delete(this);
	}
	
	public static Query<User> all() {
        return all(User.class);
	}
	
	public static int count() {
		return all().count();
	}
	
	public static List<User> findAll()  {
		return all().fetch();
	}
	
	public static List<User> paginate(Query<User> query, int offset, int limit) {
		return query.fetch(limit, offset);
	}
	
	public static User findById(Long id) {
		User user = all().filter("id", id).get();
		if(user!=null) user.get();
		return user;
	}
	
	public static User findByUsername(String username) {
		User user = all().filter("username =", username).get();
		if(user!=null) user.get();
		return user;
	}
	
	static List<Profile> getProfiles(User user) {
		return UserProfile.findByUser(user);
	}
	
	public static int countAdmins() {
		return UserProfile.count(Profile.findByType(Profile.ProfileType.ADMIN));
	}
	
	public static int countInternals() {
		return UserProfile.count(Profile.findByType(Profile.ProfileType.INTERNSHIP));
	}
	
	public static int countFamiliars() {
		return UserProfile.count(Profile.findByType(Profile.ProfileType.FAMILIAR));
	}
	
	public static List<User> getAdmins() {
		List<User> users = UserProfile.findByProfile(Profile.findByType(Profile.ProfileType.ADMIN));
		return users;
	}
	
	public static List<User> getAdmins(int offset, int limit) {
		List<User> users = UserProfile.findByProfile(Profile.findByType(Profile.ProfileType.ADMIN), offset, limit);
		return users;
	}
	
	public static List<User> getInternals(int offset, int limit) {
		List<User> users = UserProfile.findByProfile(Profile.findByType(Profile.ProfileType.INTERNSHIP), offset, limit);
		return users;
	}
	
	public static List<User> getFamiliars(int offset, int limit) {
		List<User> users = UserProfile.findByProfile(Profile.findByType(Profile.ProfileType.FAMILIAR), offset, limit);
		return users;
	}

	public static void addProfile(User user, Profile profile) {
		UserProfile userProfile = new UserProfile(user, profile);
		userProfile.insert();
	}
	
	public boolean isAdmin() {
		for(Profile profile : this.getProfiles(this)) {
			if(Profile.ProfileType.ADMIN.equals(profile.type)) return true;
		}
		return false;
	}
	
	public boolean isFamiliar() {
		for(Profile profile : this.getProfiles(this)) {
			if(Profile.ProfileType.FAMILIAR.equals(profile.type)) return true;
		}
		return false;
	}
	
	public boolean isInternal() {
		for(Profile profile : this.getProfiles(this)) {
			if(Profile.ProfileType.INTERNSHIP.equals(profile.type)) return true;
		}
		return false;
	}

	
}